package com.fast.fast.common.jpa.service;

import cn.hutool.core.util.ObjectUtil;
import com.fast.fast.common.base.page.PageResultForJpa;
import org.hibernate.query.internal.NativeQueryImpl;
import org.hibernate.transform.Transformers;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;


/**
 * JPA自定义封装接口实现类
 *
 * @author lyf
 * @date 2022/01/01 00:00 周六
 */
@Service
public class JpaServiceImpl<T> implements JpaService<T> {

    @PersistenceContext
    private EntityManager em;

    /**
     * ?占位符的填充
     *
     * @param query
     * @param sql
     * @param o
     */
    private void addCond(Query query, String sql, Object[] o) {
        int index = sql.indexOf("?");
        if (index == -1) {
            return;
        }
        int count = 0;
        // 如果第一个?的位置存在,则继续判断
        while (index != -1) {
            if (ObjectUtil.isEmpty(o)) {
                query.setParameter(count + 1, null);
            } else {
                // 把对应值赋给?的所在位置
                query.setParameter(count + 1, o[count]);
            }
            // 使用substring重新整理剩余sql
            sql = sql.substring(index + 1);
            // 再次获取剩余sql中的?的位置
            index = sql.indexOf("?");
            count++;
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void save(Object o) {
        em.persist(o);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void save(Class<T> poClass, Serializable id) {
        em.persist(em.find(poClass, id));
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void saveAll(Object[] o) {
        for (Object o1 : o) {
            save(o1);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void saveAll(Class<T> poClass, Serializable[] ids) {
        for (Serializable id : ids) {
            save(poClass, id);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void del(Object o) {
        em.remove(o);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void del(Class<T> poClass, Serializable id) {
        em.remove(load(poClass, id));
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void delAll(Object[] o) {
        for (Object o1 : o) {
            del(o1);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void delAll(Class<T> poClass, Serializable[] ids) {
        for (Serializable id : ids) {
            del(poClass, id);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void update(Object o) {
        em.merge(o);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void update(Class<T> poClass, Serializable id) {
        em.merge(load(poClass, id));
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updateAll(Object[] o) {
        for (Object o1 : o) {
            em.merge(o1);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updateAll(Class<T> poClass, Serializable[] ids) {
        for (Serializable id : ids) {
            update(poClass, id);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public int executeSql(String sql, Object[] o) {
        Query query = em.createNativeQuery(sql);
        addCond(query, sql, o);
        return query.executeUpdate();
    }

    /**************************************** 以下查询 *********************************************/

    /**************************************** hql单个查询 *********************************************/

    @Transactional(readOnly = true)
    @Override
    public T load(Class<T> poClass, Serializable id) {
        return em.getReference(poClass, id);
    }

    @Transactional(readOnly = true)
    @Override
    public List<T> loadAll(Class<T> poClass, Serializable[] ids) {
        List<T> list = new ArrayList<>();
        for (Serializable id : ids) {
            list.add(load(poClass, id));
        }
        return list;
    }

    @Transactional(readOnly = true)
    @Override
    public T get(Class<T> poClass, Serializable id) {
        return em.find(poClass, id);
    }

    @Transactional(readOnly = true)
    @Override
    public List<T> getAll(Class<T> poClass, Serializable[] ids) {
        List<T> list = new ArrayList<>();
        for (Serializable id : ids) {
            list.add(get(poClass, id));
        }
        return list;
    }

    @Transactional(readOnly = true)
    @Override
    public Object findOne(String hql, Object[] o) {
        Query query = em.createQuery(hql);
        addCond(query, hql, o);
        if (query.getResultList().size() == 0) {
            return null;
        } else {
            return query.getSingleResult();
        }
    }

    /**************************************** hql批量查询 *********************************************/

    @Transactional(readOnly = true)
    @Override
    public List find(String hql, Object[] o) {
        Query query = em.createQuery(hql);
        addCond(query, hql, o);
        return query.getResultList();
    }

    @Transactional(readOnly = true)
    @Override
    public List find(String hql, Object[] o, Integer max) {
        Query query = em.createQuery(hql);
        addCond(query, hql, o);
        query.setMaxResults(max);
        return query.getResultList();
    }

    @Transactional(readOnly = true)
    @Override
    public List findByPage(String hql, PageResultForJpa<T> page, Object[] o) {
        Query query = em.createQuery(hql);
        addCond(query, hql, o);
        query.setFirstResult(Math.toIntExact((page.getCurrent() - 1) * page.getSize()));
        query.setMaxResults(Integer.parseInt(String.valueOf(page.getSize())));
        return query.getResultList();
    }

    @Transactional(readOnly = true)
    @Override
    public List findAttr(String hql, Object[] o) {
        Query query = em.createQuery(hql);
        addCond(query, hql, o);
        return query.getResultList();
    }

    /**************************************** 原生sql查询 *********************************************/

    @Transactional(readOnly = true)
    @Override
    public List findBySql(String sql, Object[] o) {
        Query query = em.createNativeQuery(sql);
        addCond(query, sql, o);
        return query.getResultList();
    }

    @Transactional(readOnly = true)
    @Override
    public List findBySql(String sql, Object[] o, Integer max) {
        Query query = em.createNativeQuery(sql);
        addCond(query, sql, o);
        query.setMaxResults(max);
        return query.getResultList();
    }

    @Transactional(readOnly = true)
    @Override
    public List findBySql(Class<T> poClass, String sql, Object[] o) {
        Query query = em.createNativeQuery(sql, poClass);
        addCond(query, sql, o);
        return query.getResultList();
    }

    @Transactional(readOnly = true)
    @Override
    public List findBySql(Class<T> poClass, String sql, Object[] o, Integer max) {
        Query query = em.createNativeQuery(sql, poClass);
        addCond(query, sql, o);
        query.setMaxResults(max);
        return query.getResultList();
    }

    @Transactional(readOnly = true)
    @Override
    public List findBySqlNoMapping(Class<T> poClass, String sql, Object[] o) {
        Query query = em.createNativeQuery(sql);
        addCond(query, sql, o);
        query.unwrap(NativeQueryImpl.class).setResultTransformer(Transformers.aliasToBean(poClass));
        return query.getResultList();
    }

    @Transactional(readOnly = true)
    @Override
    public List findBySqlNoMapping(Class<T> poClass, String sql, Object[] o, Integer max) {
        Query query = em.createNativeQuery(sql);
        addCond(query, sql, o);
        query.unwrap(NativeQueryImpl.class).setResultTransformer(Transformers.aliasToBean(poClass));
        query.setMaxResults(max);
        return query.getResultList();
    }

    /***************** 原生sql分页查询 *****************/

    @Transactional(readOnly = true)
    @Override
    public List findBySqlPage(Class<T> poClass, String sql, PageResultForJpa<T> page, Object[] o) {
        Query query = em.createNativeQuery(sql, poClass);
        addCond(query, sql, o);
        query.setFirstResult(Math.toIntExact((page.getCurrent() - 1) * page.getSize()));
        query.setMaxResults(Integer.parseInt(String.valueOf(page.getSize())));
        return query.getResultList();
    }

    @Transactional(readOnly = true)
    @Override
    public List findBySqlNoMappingPage(Class<T> poClass, String sql, PageResultForJpa<T> page, Object[] o) {
        Query query = em.createNativeQuery(sql);
        addCond(query, sql, o);
        query.unwrap(NativeQueryImpl.class).setResultTransformer(Transformers.aliasToBean(poClass));
        query.setFirstResult(Math.toIntExact((page.getCurrent() - 1) * page.getSize()));
        query.setMaxResults(Integer.parseInt(String.valueOf(page.getSize())));
        return query.getResultList();
    }

    /***************** 原生sql唯一查询 *****************/

    @Transactional(readOnly = true)
    @Override
    public Object findOneBySql(String sql, Object[] o) {
        Query query = em.createNativeQuery(sql);
        addCond(query, sql, o);
        if (query.getResultList().size() == 0) {
            return null;
        } else {
            return query.getSingleResult();
        }
    }

    @Override
    public Object findOneBySql(Class<T> poClass, String sql, Object[] o) {
        Query query = em.createNativeQuery(sql, poClass);
        addCond(query, sql, o);
        if (query.getResultList().size() == 0) {
            return null;
        } else {
            return query.getSingleResult();
        }
    }

    @Transactional(readOnly = true)
    @Override
    public Object findOneBySqlNoMapping(Class<T> poClass, String sql, Object[] o) {
        Query query = em.createNativeQuery(sql);
        addCond(query, sql, o);
        query.unwrap(NativeQueryImpl.class).setResultTransformer(Transformers.aliasToBean(poClass));
        return query.getSingleResult();
    }
}
